#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/kmod.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/uaccess.h>



/* 1-确定主设备号                                                                 */
static int major = 0;
static struct gpio_desc *ds18b20_data_pin;
static struct class *ds18b20_class;

//static DECLARE_WAIT_QUEUE_HEAD(ds18b20_waitqueue);
struct spi_transfer
/* 保留3位小数点，并扩大1000倍 */
static int ds18b20_calculate_temperature(char temph, char templ)
{
    int temp_int, int_calc, decimal, temp_final;
    
    if(temph>0x7f) // 负数温度
    {
        int_calc = ((temph<<8) | templ);
        int_calc = (~int_calc) + 1;
        temp_int = (int_calc>>4)&0x7F;
        decimal = (int_calc&0xF)*62;
        temp_final = (int)(temp_int*1000+decimal);
        return (-temp_final);
    }
    else 
    {
        int_calc = ((temph<<8) | templ);
        temp_int = (int_calc>>4)&0x7F;
        decimal = (int_calc&0xF)*62;
        temp_final = (int)(temp_int*1000+decimal);
        return (temp_final);
    }
}

static void ds18b20_delay_us(int us)
{
    u64 pre_count = 0;
    u64 last = 0;

    pre_count = ktime_get_boot_ns();
    while(1)
    {
        last = ktime_get_boot_ns();
        if (last - pre_count > us*1000)
            break;
    }
}

static int ds18b20_write_byte(unsigned char data)
{
    // 写数据以LSB进行
    int i;
    for(i = 0; i<8; i++)
    {
        if(data & 1<<i)
        {
            gpiod_direction_output(ds18b20_data_pin, 0);
            ds18b20_delay_us(2);
            
            gpiod_direction_input(ds18b20_data_pin);
            ds18b20_delay_us(60);
        }
        else 
        {
            gpiod_direction_output(ds18b20_data_pin, 0);
            ds18b20_delay_us(60);
            gpiod_direction_input(ds18b20_data_pin);
            ds18b20_delay_us(2);  // 时隙2us
        }
    }
    return 0;
}

static char ds18b20_read_byte(void)
{
    // 读数据以LSB进行
    int i;
    char data = 0;
    for(i = 0; i<8; i++)
    {
        gpiod_direction_output(ds18b20_data_pin, 0);
        ds18b20_delay_us(2);

        gpiod_direction_input(ds18b20_data_pin);
        ds18b20_delay_us(7);    // 如果读数都是0，调整此数值

        if(gpiod_get_value(ds18b20_data_pin))
            data |= (1<<i);

        ds18b20_delay_us(60);
    }

    return data;
}



static int ds18b20_wait_for_ack(void)
{
    int timeout;

    timeout = 240;
    /* 等待高电平结束,高电平持续15-60us */
    while(gpiod_get_value(ds18b20_data_pin) && --timeout)
    {
        udelay(1);
    }
    if(!timeout)
        return -1;

    timeout = 300;
    /* 等待低电平响应信号,持续60-240us */
    while(!gpiod_get_value(ds18b20_data_pin) && --timeout)
    {
        udelay(1);
    }
    if(!timeout)
        return -1;

    return 0;
}

static int ds18b20_start(void)
{
    int ret;
    gpiod_direction_output(ds18b20_data_pin, 0);
    ds18b20_delay_us(480);
    ret = gpiod_direction_input(ds18b20_data_pin);  // 改为输入方向
    
    if(ds18b20_wait_for_ack())
        return -1;
    else 
        return 0;

}


static int sensor_ds18b20_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offsets)
{
    return size;
}

static int sensor_ds18b20_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
    int len, err, ret;
    int l_temperature;
    char temp_l, temp_h;
    unsigned long flags;

    local_irq_save(flags); // 关中断
    
    /* 18b20读数据流程 */

    /* 1、启动信号 */
    if(ds18b20_start())
    {
        /* 启动失败 */
        gpiod_direction_output(ds18b20_data_pin, 1);
        local_irq_restore(flags);
        pr_err("can't not receive a ACK !!!\n");
        return -ENODEV;
    }
    /* 2、写rom指令（单个使用skip rom[CCh]） */
    ds18b20_write_byte(0xCC);
    
    /* 3、写温度转换指令[44h] */
    ds18b20_write_byte(0x44);
    
    /* 4、等待转换完成 */
    gpiod_direction_output(ds18b20_data_pin, 1);

    local_irq_restore(flags); // 恢复中断

    set_current_state(TASK_INTERRUPTIBLE);
    schedule_timeout(HZ); // 等待1s
    
    /* 5、读数据 */
    local_irq_save(flags); // 关中断
    if(ds18b20_start())
    {
        /* 启动失败 */
        gpiod_direction_output(ds18b20_data_pin, 1);
        local_irq_restore(flags);
        pr_err("can't not receive a ACK !!!\n");
        return -ENODEV;
    }
    ds18b20_write_byte(0xCC);
    
    /* 读暂存器指令[BEh] */
    ds18b20_write_byte(0xBE);
    temp_l = ds18b20_read_byte();
    temp_h = ds18b20_read_byte();
    ds18b20_start();    // 终止读数据
    local_irq_restore(flags); // 恢复中断
    
    /* 6、转化数据 */
    l_temperature = ds18b20_calculate_temperature(temp_h, temp_l);
    gpiod_direction_output(ds18b20_data_pin, 1);
    printk("%s line %d: temperature = %d, temp_h = 0x%02x ,temp_l = 0x%02x\n", \
            __FUNCTION__, __LINE__, l_temperature, temp_h, temp_l);
    err = copy_to_user(buf, &l_temperature, 4);

    return 4;
}


/* 按键读取驱动 file_operations */
static struct file_operations xxh_ds18b20_drv = {
    .owner = THIS_MODULE,
    .write = sensor_ds18b20_drv_write,
    .read  = sensor_ds18b20_drv_read,
};


/*
 * 4、从platform device获取GPIO
 *    把file_operations结构体告诉内核：注册驱动程序
 */
static int xxh_sensor_ds18b20_probe(struct platform_device *pdev)
{
    int err;

    printk("%s line %d: probe DHT11 sensor.\n", __FUNCTION__, __LINE__);

    /* 设备节点获取 */
    ds18b20_data_pin = gpiod_get(&pdev->dev, NULL, GPIOD_OUT_HIGH);

    return 0;
}

static int xxh_sensor_ds18b20_remove(struct platform_device *dev)
{
    printk("%s line %d\n", __FUNCTION__, __LINE__);

    /* 设备节点释放 */
    //free_irq(sensor_ds18b20->irq, &sensor_ds18b20);
    gpiod_put(ds18b20_data_pin);

    return 0;
}


static const struct of_device_id sensor_ds18b20_ids[] = {
    {.compatible = "xxh,sensor_wireone_bus"},
    {},
};

/* 1、定义platform_drver结构体 */
static struct platform_driver xxh_sensor_ds18b20_driver = {
    .probe  = xxh_sensor_ds18b20_probe,
    .remove = xxh_sensor_ds18b20_remove,
    .driver = {
        .name   = "xxh_sensor_ds18b20",
        .of_match_table = sensor_ds18b20_ids,
    },
};


static int __init sensor_ds18b20_init(void)
{
    int err;
    printk("%s line %d\n", __FUNCTION__, __LINE__);

    /* 注册file_operations结构体 */
    major = register_chrdev(0, "xxh_ds18b20", &xxh_ds18b20_drv);
    ds18b20_class = class_create(THIS_MODULE, "xxhds18b20");  /* /sys/class/xxhds18b20 */
    if (IS_ERR(ds18b20_class)) {
        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
        unregister_chrdev(major, "xxh_ds18b20");
        return -1;
    }

    device_create(ds18b20_class, NULL, MKDEV(major, 0), NULL, "xxhds18b20"); /* /dev/xxhds18b20 */
    
    err = platform_driver_register(&xxh_sensor_ds18b20_driver);

    return err;
}


static void __exit sensor_ds18b20_exit(void)
{
    printk("%s line %d\n", __FUNCTION__, __LINE__);
    
    device_destroy(ds18b20_class, MKDEV(major, 0));
    class_destroy(ds18b20_class);
    unregister_chrdev(major, "xxh_ds18b20");

    platform_driver_unregister(&xxh_sensor_ds18b20_driver);
    return;
}

/* 7. 其他完善：提供设备信息，自动创建设备节点                                     */

module_init(sensor_ds18b20_init);
module_exit(sensor_ds18b20_exit);

MODULE_AUTHOR("xxh <1293793140@qq.com>");
MODULE_LICENSE("GPL");

